<template>
  <e-select
    ref="select"
    class="haloe-auto-complete"
    filterable
    remote
    :auto-complete="!enabledLazyLoad"
    :label="label"
    :disabled="itemDisabled"
    :clearable="clearable"
    :placeholder="placeholder"
    :size="size"
    :placement="placement"
    :default-label="defaultLabel"
    :model-value="currentSelectedValue"
    :transfer-class-name="transferClassName"
    :remote-method="remoteMethod"
    :transfer="transfer"
    :capture="capture"
    :eventsEnabled="eventsEnabled"
    @on-select="handleSelect"
    @on-clickoutside="handleClickOutside"
  >
    <template #input>
      <slot name="input">
        <e-input
          :element-id="elementId"
          ref="input"
          v-model="currentInputValue"
          :name="name"
          :placeholder="placeholder"
          :disabled="itemDisabled"
          :size="size"
          :icon="inputIcon"
          @on-click="handleClear"
          @on-focus="handleFocus"
          @on-blur="handleBlur"
        ></e-input>
      </slot>
    </template>
    <template #loading>
      <slot name="loading">
        <div
          :class="spinPrefixCls"
          :style="styles"
          ref="spin"
          v-show="loading && dropVisible"
        >
          <e-spin
            type="circular"
            size="middle">
            <slot name="spin-text">
              <span>加载中</span>
            </slot>
          </e-spin>
        </div>
      </slot>
    </template>
    <template #autoComplete>
      <slot>
        <template v-if="hasLabel">
          <e-option
            v-for="item in filteredData"
            :value="item.value"
            :label="item.label"
            :key="item.value"
            :disabled="item.disabled"
          >
            <HighlightKeyword
              :highlight="highlight"
              :ignoreCase="ignoreCase"
              :keyword="currentInputValue"
              :text="item.label"
              :disabled="item.disabled"
            />
          </e-option>
        </template>
        <template v-else>
          <e-option
            v-for="item in filteredData"
            :value="item"
            :key="item"
          >
            <HighlightKeyword
              :highlight="highlight"
              :ignoreCase="ignoreCase"
              :keyword="currentInputValue"
              :text="item"
              v-if="typeof item === 'string'"
            />
            <template v-else>{{ item }}</template>
          </e-option>
        </template>
      </slot>
    </template>

    <template #remote-result>
      <div
        v-if="enabledLazyLoad"
        :class="prefixCls + '-lazy-load-tip'"
      >
        <span
          v-if="isErrorValue"
          :class="prefixCls + '-lazy-load-tip-error'"
          @click="handleError"
        >{{ localeErrorText }}</span>
        <span
          v-else-if="isFinishedValue"
          :class="prefixCls + '-lazy-load-tip-finished'"
        >{{ localeFinishedText }}</span>
      </div>
    </template>
  </e-select>
</template>
<script>
import { getCurrentInstance, nextTick } from 'vue'
import eSelect from '../select/select.vue'
import eOption from '../select/option.vue'
import eInput from '../input/input.vue'
import eSpin from '../spin/spin.vue'
import HighlightKeyword from './highlight-keyword'
import { oneOf } from '../../utils/assist'
import mixinsForm from '../../mixins/form'
import { on, off } from '../../utils/dom'
import { getStyle, debounce } from '../../utils/assist'
import bus from 'vue3-eventbus'
import Locale from '../../mixins/locale';

const prefixCls = 'haloe-auto-complete';
const spinPrefixCls = `${prefixCls}-spin-wrapper`;

export default {
  name: 'eAutoComplete',
  mixins: [mixinsForm,Locale],
  components: {
    eSelect,
    eOption,
    eInput,
    eSpin,
    HighlightKeyword
  },
  emits: [
    'update:modelValue',
    'on-change',
    'on-search',
    'on-load-more',
    'on-select',
    'on-focus',
    'on-blur',
    'on-clear',
  ],
  props: {
    modelValue: {
      type: [String, Number],
      default: '',
    },
    defaultValue: {
      type: [String, Number],
      default: '',
    },
    label: {
      type: [String, Number],
      default: '',
    },
    data: {
      type: Array,
      default: () => [],
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    clearable: {
      type: Boolean,
      default: false,
    },
    placeholder: {
      type: String,
    },
    size: {
      validator(value) {
        return oneOf(value, ['small', 'middle', 'large', 'default'])
      },
      default() {
        const global = getCurrentInstance().appContext.config.globalProperties
        return !global.$HALOE || global.$HALOE.size === ''
          ? 'default'
          : global.$HALOE.size
      },
    },
    icon: {
      type: String,
    },
    filterMethod: {
      type: [Function, Boolean],
      default: false,
    },
    placement: {
      validator(value) {
        return oneOf(value, [
          'top',
          'bottom',
          'top-start',
          'bottom-start',
          'top-end',
          'bottom-end',
        ])
      },
      default: 'bottom-start',
    },
    transfer: {
      type: Boolean,
      default() {
        const global = getCurrentInstance().appContext.config.globalProperties
        return !global.$HALOE || global.$HALOE.transfer === ''
          ? false
          : global.$HALOE.transfer
      },
    },
    name: {
      type: String,
    },
    elementId: {
      type: String,
    },
    transferClassName: {
      type: String,
    },
    // 4.6.0
    capture: {
      type: Boolean,
      default() {
        const global = getCurrentInstance().appContext.config.globalProperties
        return !global.$HALOE ? true : global.$HALOE.capture
      },
    },
    // 4.6.0
    eventsEnabled: {
      type: Boolean,
      default: false,
    },
    highlight: {
      type: Boolean,
      default: false,
    },
    ignoreCase: {
      type: Boolean,
      default: true,
    },
    loading: {
      type: Boolean,
      default: false
    },
    enabledLazyLoad: {
      type: Boolean,
      default: false,
    },
    isError: {
      type: Boolean,
      default: false
    },
    errorText: {
      type: String,
      // default: '请求失败，点击重新加载'
    },
    isFinished: {
      type: Boolean,
      default: false
    },
    finishedText: {
      type: String,
      // default: '没有更多了'
    },
  },
  data() {
    return {
      isErrorValue: false,
      isFinishedValue: false,
      dropVisible: false,
      debounceHandleScroll: null,
      prefixCls,
      spinPrefixCls,
      styles: {},
      currentInputValue: this.modelValue,
      defaultLabel: this.modelValue ? String(this.modelValue) : '',
      currentSelectedValue: this.defaultValue,
      disableEmitChange: false, // for Form reset
    }
  },
  computed: {
    hasLabel() {
      return this.data?.some(item => item.label)
    },
    inputIcon() {
      let icon = ''
      if (this.clearable && this.currentInputValue && !this.disabled) {
        icon = 'close-circle-filled'
      } else if (this.icon) {
        icon = this.icon
      }
      return icon
    },
    filteredData() {
      if (this.filterMethod) {
        return this.data.filter((item) =>
          this.filterMethod(this.currentInputValue, item)
        )
      } else {
        return this.data
      }
    },
    localeErrorText(){
      if(this.errorText===undefined){
        return this.t('i.autocomplete.errorText')
      }else{
        return this.errorText
      }
    },
    localeFinishedText(){
      if(this.finishedText===undefined){
        return this.t('i.autocomplete.finishedText')
      }else{
        return this.finishedText
      }
    }
  },
  watch: {
    isErrorValue(val) {
      if (val) {
        nextTick(() => {
          const dropdownDom = this.$refs.select.$refs.dropdown.$refs.drop
          dropdownDom.scrollTop = dropdownDom.scrollHeight
        })
      }
    },
    isError(val) {
      this.isErrorValue = val
    },
    isFinished(val) {
      this.isFinishedValue = val
    },
    modelValue(val) {
      if (this.currentInputValue !== val) {
        this.disableEmitChange = true
      }
      this.currentInputValue = val
    },
    currentInputValue(val) {
      if (!val) {
        this.currentSelectedValue = ''
      }
      this.$refs.select.setQuery(val)
      this.$emit('update:modelValue', val)
      if (this.disableEmitChange) {
        this.disableEmitChange = false
        return
      }
      this.$emit('on-change', val)
      this.handleFormItemChange('change', val)
    },
  },
  methods: {
    remoteMethod(query) {
      this.isErrorValue = false
      this.isFinishedValue = false
      this.$emit('on-search', query)
    },
    handleSelect(option) {
      const val = (this.hasLabel || this.enabledLazyLoad) ? option.label : option.value
      if (val === undefined || val === null) return
      this.currentSelectedValue = option.value
      this.currentInputValue = val
      this.$refs.input.blur()
      delete option['tag']
      this.$emit('on-select', (this.hasLabel || this.enabledLazyLoad) ? option : val)
    },
    handleFocus(event) {
      this.$emit('on-focus', event)
    },
    handleBlur(event) {
      this.$emit('on-blur', event)
    },
    handleClear() {
      if (!this.clearable) return
      this.currentInputValue = ''
      this.currentSelectedValue = ''
      this.$refs.select.reset()
      this.$emit('on-clear')
    },
    handleClickOutside() {
      nextTick(() => {
        this.$refs.input.blur()
      })
    },
    handleResize() {
      setTimeout(() => {
        const dropdownDom = this.$refs.select.$refs.dropdown.$refs.drop
        const spinDom = this.$refs.spin
        if (dropdownDom && spinDom) {
          this.styles['height'] = `${dropdownDom.offsetHeight}px`
          this.styles['width'] = `${dropdownDom.offsetWidth}px`
          const top = parseInt(getStyle(dropdownDom, 'top'))
          const left = parseInt(getStyle(dropdownDom, 'left'))
          if (!isNaN(top)) {
            this.styles['top'] = `${top}px`
          }
          if (!isNaN(left)) {
            this.styles['left'] = `${left}px`
          }
          if (this.transfer) {
            this.styles['z-index'] = parseInt(getStyle(dropdownDom, 'zIndex')) + 1
          }
        }
      })
    },
    handleScroll() {
      const dom = this.$refs.select.$refs.dropdown.$refs.drop
      const clientHeight = dom.clientHeight;
      const scrollTop = dom.scrollTop;
      const scrollHeight = dom.scrollHeight;
      if (clientHeight + scrollTop + 1 >= scrollHeight) {
        if (this.enabledLazyLoad && !this.isFinished && !this.isError) {
          this.$emit('on-load-more', this.currentInputValue)
        }
      }
    },
    handleError() {
      this.$emit('on-load-more', this.currentInputValue)
    },
    initDropVisible() {
      this.dropVisible = this.$refs.select.dropVisible
      this.$watch(
        () => {
          return this.$refs.select.dropVisible
        },
        (val) => {
          this.dropVisible = val
        }
      )
    }
  },
  mounted() {
    const dropDom = this.$refs.select.$refs.dropdown.$refs.drop
    this.debounceHandleScroll = debounce(this.handleScroll, 500)
    on(dropDom, 'scroll', this.debounceHandleScroll)
    bus.on('on-dropdown-resize', this.handleResize)
    this.initDropVisible()
  },
  beforeUnmount () {
    const dropDom = this.$refs.select.$refs.dropdown.$refs.drop
    off(dropDom, 'scroll', this.debounceHandleScroll)
    bus.off('on-dropdown-resize', this.handleResize)
  },
}
</script>
